package com.ror.game;

import com.ror.domain.Poker;
import com.ror.domain.Seat;
import com.ror.event.*;
import com.ror.util.AnimationUtil;
import com.ror.util.AssetsUtil;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;

public class PokerLabel extends JLabel implements MouseListener, GameObserver {
    //注册的本类观察者
    private static final List<GameObserver> observers = new ArrayList<>();
    //扑克牌组件出生点
    private static final Point POKER_SPAWN_POINT = new Point(400, 200);
    private static final int UP_HEIGHT = 20; //弹起的高度
    private static final int HOVER_UP_HEIGHT = 5; //悬停时弹起的高度

    private final Poker card; //装载的扑克牌
    private boolean isFront = false; //是否为正面
    private boolean isInteractable = false; //主视角是否可交互

    private Point baseLocation; //组件的基准位置(原位)
    private boolean isUp = false; //是否选中并弹起
    private boolean isBeingHovered = false; //鼠标是否悬停

    public PokerLabel(Poker card) {
        this.card = card;
        initLabel();

    }

    private void initLabel() {
        //初始化贴图为牌背贴图
        this.setIcon(new ImageIcon(AssetsUtil.POKER_REAR_IMAGE_PATH));
        //初始化自身布局
        this.setSize(AssetsUtil.POKER_IMAGE_WIDTH, AssetsUtil.POKER_IMAGE_HEIGHT);
        this.setHorizontalAlignment(SwingConstants.CENTER);
        this.setVerticalAlignment(SwingConstants.CENTER);
//        this.setBorder(new BevelBorder(BevelBorder.LOWERED));
        //添加鼠标事件监听
        this.addMouseListener(this);
    }

    private void updateIcon() {
        if (!isFront) this.setIcon(new ImageIcon(AssetsUtil.POKER_REAR_IMAGE_PATH));
        else this.setIcon(new ImageIcon(AssetsUtil.assetPathOf(this.card)));
        notifyObservers(GameEventType.COMPONENT_UPDATED, null);
    }

    private void updateLocation() {
        int offsetY = 0;
        if (isBeingHovered) offsetY = -HOVER_UP_HEIGHT;
        if (isUp) offsetY = -UP_HEIGHT; //选中的会覆盖悬停的
        this.setLocation(baseLocation.x, baseLocation.y + offsetY);
        notifyObservers(GameEventType.COMPONENT_UPDATED, null);
    }

    //翻面
    public void flip() {
        isFront = !isFront;
        updateIcon();
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        if (!isInteractable) return;
        isUp = !isUp;
        updateLocation();
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        if (!isInteractable) return;
        isBeingHovered = true;
        updateLocation();
    }

    @Override
    public void mouseExited(MouseEvent e) {
        //选中的弹起高度会覆盖悬停的，因此选中时无需从悬停落下
        if (!isInteractable || isUp) return;
        isBeingHovered = false;
        updateLocation();
    }

    public Poker getCard() {
        return this.card;
    }

    public void setBaseLocation(Point baseLocation) {
        //防御性复制，防止再被更改
        this.baseLocation = new Point(baseLocation);
    }

    public void addObserver(GameObserver observer) {
        observers.add(observer);
    }

    public void removeObserver(GameObserver observer) {
        observers.remove(observer);
    }

    protected void notifyObservers(GameEventType eventType, GameEventArgs eventArgs) {
        GameEvent e = new GameEvent(eventType, eventArgs);
        observers.forEach(o -> o.onGameUpdated(e));
    }

    public void onCardDistributed(CardDistributedEventArgs args) {
        //计算放牌的位置
        Point toLocation = args.seat.getBaseLocation();
        toLocation.move(toLocation.x + args.seat.getOffsetX(),
                toLocation.y + args.seat.getOffsetY());
        //主视角只能交互自己的牌
        isInteractable = args.seat == Seat.MIDDLE;
        //翻开主视角的牌和底牌
        if (isInteractable || args.seat == Seat.HAND) flip();
        AnimationUtil.move(this, POKER_SPAWN_POINT, toLocation);
    }

    @Override
    public void onGameUpdated(GameEvent e) {
        if (e.getType() == GameEventType.MOVE_ANIMATION_DONE) {
            //更新基准位置
            this.baseLocation = this.getLocation();
        }
    }
}
